package de.szut.xml;

import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by dzebcuks on 22.08.13.
 */
public abstract class XmlParser {

    /**
     * Starts the initial xml data parse
     *
     * @param in - input stream of the xml to parse
     * @return a list of the parsed objects
     * @throws XmlPullParserException
     * @throws IOException
     */
    public List<Object> parse(InputStream in) throws XmlPullParserException, IOException {
        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
            parser.setInput(in, null);
            parser.nextTag();
            return readFeed(parser);
        } finally {
            in.close();
        }
    }

    /**
     * Reads the xml file tag by tag and builds ability-objects out of the found data
     *
     * @param parser
     * @return
     * @throws XmlPullParserException
     * @throws IOException
     */
    private List<Object> readFeed(XmlPullParser parser) throws XmlPullParserException,
            IOException {
        List<Object> objects = new ArrayList<Object>();
        parser.require(XmlPullParser.START_TAG, null, this.getRequiredTag());
        while (parser.next() != XmlPullParser.END_DOCUMENT) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();

            // Looks up for the necessary tags
            if (this.getNecessaryTags().contains(name)) {
                this.handleTag(objects, parser, name);
            } else {
                skip(parser);
            }
        }
        return objects;
    }


    /**
     * Skips all not necessary tags to increase the performance
     *
     * @param parser
     * @throws org.xmlpull.v1.XmlPullParserException
     * @throws java.io.IOException
     */
    private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            throw new IllegalStateException();
        }
        int depth = 1;
        while (depth != 0) {
            switch (parser.next()) {
                case XmlPullParser.END_TAG:
                    depth--;
                    break;
                case XmlPullParser.START_TAG:
                    depth++;
                    break;
            }
        }
    }

    /**
     * Should return the required tag, to parse this XML. If the tag is not available,
     * the parse operation will be aborted
     *
     * @return
     */
    protected abstract String getRequiredTag();

    /**
     * Returns a list of all tags, to check. Every tag, that is not contained by this list,
     * will be simply skipped.
     *
     * @return
     */
    protected abstract List<String> getNecessaryTags();

    /**
     * Parses the data out of the found tag and adds it to the list of objects.
     *
     * @param objects
     * @param parser
     * @param tagName
     */
    protected abstract void handleTag(List<Object> objects, XmlPullParser parser, String tagName);
}
